package Builder;

import java.io.Reader;
import compositeExpression.Cond;
import compositeExpression.Expr;
import compositeExpression.Fact;
import compositeExpression.Factb;
import compositeExpression.Term;
import compositeExpression.Termb;
import compositeExpression.Termp;
import compositeExpression_Fact.FactExpr;
import compositeExpression_Fact.FactId;
import compositeExpression_Fact.FactNum;
import compositeExpression_Factb.FactbCond;
import compositeExpression_Factb.FactbNot;
import compositeExpression_Factb_FactbExpr.RelopEq;
import compositeExpression_Factb_FactbExpr.RelopGe;
import compositeExpression_Factb_FactbExpr.RelopGt;
import compositeExpression_Factb_FactbExpr.RelopLe;
import compositeExpression_Factb_FactbExpr.RelopLt;
import compositeExpression_Factb_FactbExpr.RelopNeq;
import compositeExpression_Term.MinusTerm;
import compositeExpression_Term.PlusTerm;
import compositeExpression_Termp.DivTermp;
import compositeExpression_Termp.MulTermp;
import compositeExpression_Termp.RemTermp;


public class RecursiveExpressionBuilder implements BuilderIF {
	
	LexicalAnalyzer lexer;
	Cond root;
	Symbles symble;

	public RecursiveExpressionBuilder(Reader in) {

        lexer = new LexicalAnalyzer(in);
        root = cond();
        atteso(Symbles.EOF);
    }

    @Override
    public Cond cond() {

        Cond cond = new Cond(termb());

        while (symble == Symbles.OR) {

            cond.add(termb());
        }

        return cond;
    }// cond

    @Override
    public Termb termb() {

        Termb termb = new Termb(factb());

        while (symble == Symbles.AND) {

            termb.add(factb());
        }

        return termb;

    }// termb

    @Override
    public Factb factb() {

        symble = lexer.nextSymble();

        if (symble == Symbles.NOT) {

            return new FactbNot(factb());
        } else if (symble == Symbles.TONDA_APERTA) {

            Cond cond = cond();
            atteso(Symbles.TONDA_CHIUSA);
            return new FactbCond(cond);
        } else {

            Expr left = expr();
            Expr right = null;

            if (symble == Symbles.EQ) {

                symble = lexer.nextSymble();
                right = expr();
                return new RelopEq(left, right);
            } else if (symble == Symbles.NEQ) {

                symble = lexer.nextSymble();
                right = expr();
                return new RelopNeq(left, right);
            } else if (symble == Symbles.GT) {

                symble = lexer.nextSymble();
                right = expr();
                return new RelopGt(left, right);
            } else if (symble == Symbles.GE) {

                symble = lexer.nextSymble();
                right = expr();
                return new RelopGe(left, right);
            } else if (symble == Symbles.LT) {

                symble = lexer.nextSymble();
                right = expr();
                return new RelopLt(left, right);
            } else if (symble == Symbles.LE) {

                symble = lexer.nextSymble();
                right = expr();
                return new RelopLe(left, right);
            } else {

                throw new IllegalStateException("Atteso uno dei seguenti simboli: EQ, NEQ, LT, LE, GT, GE");
            }

        }
    }// factb

    @Override
    public Expr expr() {

        Expr expr = new Expr(term());

        while (symble == Symbles.MINUS || symble == Symbles.PLUS) {

            expr.add(term());
        }
        return expr;
    }// expr

    @Override
    public Term term() {

        Term term;

        if (symble == Symbles.PLUS) {

            symble = lexer.nextSymble();
            term = new PlusTerm(termp());
        } else if (symble == Symbles.MINUS) {

            symble = lexer.nextSymble();
            term = new MinusTerm(termp());
        } else {

            term = new Term(termp());
        }

        while (symble == Symbles.MULT || symble == Symbles.DIV || symble == Symbles.REM) {

            term.add(termp());
        }
        return term;
    }// term

    @Override
    public Termp termp() {

        Termp termp;

        if (symble == Symbles.MULT) {

            symble = lexer.nextSymble();
            termp = new MulTermp(fact());
        } else if (symble == Symbles.DIV) {

            symble = lexer.nextSymble();
            termp = new DivTermp(fact());
        } else if (symble == Symbles.REM) {

            symble = lexer.nextSymble();
            termp = new RemTermp(fact());
        } else {

            termp = new Termp(fact());
        }

        while (symble == Symbles.POWER) {

            symble = lexer.nextSymble();
            termp.add(fact());
        }
        return termp;
    }// termp

    @Override
    public Fact fact() {

        Fact res;

        if (symble == Symbles.ID) {

            res = new FactId(lexer.getString());
            symble = lexer.nextSymble();
        } else if (symble == Symbles.NUM) {

            res = new FactNum(lexer.getString());
            symble = lexer.nextSymble();
        } else if (symble == Symbles.QUADRA_APERTA) {

            symble = lexer.nextSymble();
            Expr expr = expr();
            atteso(Symbles.QUADRA_CHIUSA);
            res = new FactExpr(expr);

        } else {

            throw new IllegalStateException("Atteso NUM, ID o EXPR");
        }

        return res;
    }// fact

    private void atteso(Symbles s) {

        if (symble != s) {

            String msg = " trovato " + symble + " mentre si attendeva " + s;
            throw new IllegalStateException(msg);
        }
        symble = lexer.nextSymble();
    }// atteso

    public Cond getCond() {

        return root;
    }// getCond

}
